Line data Source code
1 : /* SPDX-License-Identifier: MIT OR GPL-3.0-only */
2 : /* conn.c
3 : ** strophe XMPP client library -- connection object functions
4 : **
5 : ** Copyright (C) 2005-2009 Collecta, Inc.
6 : **
7 : ** This software is provided AS-IS with no warranty, either express
8 : ** or implied.
9 : **
10 : ** This program is dual licensed under the MIT or GPLv3 licenses.
11 : */
12 :
13 : /** @file
14 : * Connection management.
15 : */
16 :
17 : /** @defgroup Connections Connection management
18 : * These functions manage a connection object.
19 : *
20 : * A part of those functions is listed under the \ref TLS section.
21 : */
22 :
23 : #include <errno.h>
24 : #include <stdarg.h>
25 : #include <string.h>
26 : #include <limits.h>
27 :
28 : #include "strophe.h"
29 :
30 : #include "common.h"
31 : #include "util.h"
32 : #include "parser.h"
33 :
34 : #ifndef DEFAULT_SEND_QUEUE_MAX
35 : /** @def DEFAULT_SEND_QUEUE_MAX
36 : * The default maximum send queue size. This is currently unused.
37 : */
38 : #define DEFAULT_SEND_QUEUE_MAX 64
39 : #endif
40 : #ifndef DISCONNECT_TIMEOUT
41 : /** @def DISCONNECT_TIMEOUT
42 : * The time to wait (in milliseconds) for graceful disconnection to
43 : * complete before the connection is reset. The default is 2 seconds.
44 : */
45 : #define DISCONNECT_TIMEOUT 2000 /* 2 seconds */
46 : #endif
47 : #ifndef CONNECT_TIMEOUT
48 : /** @def CONNECT_TIMEOUT
49 : * The time to wait (in milliseconds) for a connection attempt to succeed
50 : * or error. The default is 5 seconds.
51 : */
52 : #define CONNECT_TIMEOUT 5000 /* 5 seconds */
53 : #endif
54 :
55 : #ifndef KEEPALIVE_TIMEOUT
56 : /** @def KEEPALIVE_TIMEOUT
57 : * The time (in seconds) the connection needs to remain idle before TCP starts
58 : * sending keepalive probes, if the socket option SO_KEEPALIVE has been set on
59 : * this socket.
60 : * c.f. `TCP_KEEPIDLE` in `man 7 tcp` for linux, FreeBSD and some others or
61 : * `TCP_KEEPALIVE` on MacOS.
62 : */
63 : #define KEEPALIVE_TIMEOUT 60
64 : #endif
65 : #ifndef KEEPALIVE_INTERVAL
66 : /** @def KEEPALIVE_INTERVAL
67 : * The time (in seconds) between individual keepalive probes.
68 : * c.f. `TCP_KEEPINTVL` in `man 7 tcp`
69 : */
70 : #define KEEPALIVE_INTERVAL 30
71 : #endif
72 : #ifndef KEEPALIVE_COUNT
73 : /** @def KEEPALIVE_COUNT
74 : * The maximum number of keepalive probes TCP should send before dropping the
75 : * connection.
76 : * c.f. `TCP_KEEPCNT` in `man 7 tcp`
77 : */
78 : #define KEEPALIVE_COUNT 3
79 : #endif
80 :
81 : static int _is_connected(xmpp_conn_t *conn, xmpp_send_queue_owner_t owner);
82 : static int _disconnect_cleanup(xmpp_conn_t *conn, void *userdata);
83 : static void _reset_sm_state_for_reconnect(xmpp_conn_t *conn);
84 : static char *_conn_build_stream_tag(xmpp_conn_t *conn,
85 : char **attributes,
86 : size_t attributes_len);
87 : static int _conn_open_stream_with_attributes(xmpp_conn_t *conn,
88 : char **attributes,
89 : size_t attributes_len);
90 : static void _conn_attributes_new(xmpp_conn_t *conn,
91 : char **attrs,
92 : char ***attributes,
93 : size_t *attributes_len);
94 : static void _conn_attributes_destroy(xmpp_conn_t *conn,
95 : char **attributes,
96 : size_t attributes_len);
97 : static void _handle_stream_start(char *name, char **attrs, void *userdata);
98 : static void _handle_stream_end(char *name, void *userdata);
99 : static void _handle_stream_stanza(xmpp_stanza_t *stanza, void *userdata);
100 : static void _conn_sm_handle_stanza(xmpp_conn_t *const conn,
101 : xmpp_stanza_t *stanza);
102 : static unsigned short _conn_default_port(xmpp_conn_t *conn,
103 : xmpp_conn_type_t type);
104 : static void _conn_reset(xmpp_conn_t *conn);
105 : static int _conn_connect(xmpp_conn_t *conn,
106 : const char *domain,
107 : xmpp_conn_type_t type,
108 : xmpp_conn_handler callback,
109 : void *userdata);
110 : static void _send_valist(xmpp_conn_t *conn,
111 : const char *fmt,
112 : va_list ap,
113 : xmpp_send_queue_owner_t owner);
114 : static int _send_raw(xmpp_conn_t *conn,
115 : char *data,
116 : size_t len,
117 : xmpp_send_queue_owner_t owner,
118 : void *userdata);
119 :
120 0 : void xmpp_send_error(xmpp_conn_t *conn, xmpp_error_type_t type, char *text)
121 : {
122 0 : xmpp_stanza_t *error = xmpp_error_new(conn->ctx, type, text);
123 :
124 0 : send_stanza(conn, error, XMPP_QUEUE_STROPHE);
125 0 : }
126 :
127 : /** Create a new Strophe connection object.
128 : *
129 : * @param ctx a Strophe context object
130 : *
131 : * @return a Strophe connection object or NULL on an error
132 : *
133 : * @ingroup Connections
134 : */
135 8 : xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx)
136 : {
137 8 : xmpp_conn_t *conn = NULL;
138 8 : xmpp_connlist_t *tail, *item;
139 :
140 8 : if (ctx == NULL)
141 : return NULL;
142 :
143 8 : conn = strophe_alloc(ctx, sizeof(xmpp_conn_t));
144 8 : if (conn != NULL) {
145 8 : memset(conn, 0, sizeof(xmpp_conn_t));
146 8 : conn->ctx = ctx;
147 :
148 8 : conn->type = XMPP_UNKNOWN;
149 8 : conn->state = XMPP_STATE_DISCONNECTED;
150 :
151 8 : conn->sock = INVALID_SOCKET;
152 8 : conn->ka_timeout = KEEPALIVE_TIMEOUT;
153 8 : conn->ka_interval = KEEPALIVE_INTERVAL;
154 8 : conn->ka_count = KEEPALIVE_COUNT;
155 :
156 : /* default send parameters */
157 8 : conn->send_queue_max = DEFAULT_SEND_QUEUE_MAX;
158 :
159 : /* default timeouts */
160 8 : conn->connect_timeout = CONNECT_TIMEOUT;
161 :
162 8 : conn->lang = strophe_strdup(conn->ctx, "en");
163 8 : if (!conn->lang) {
164 0 : strophe_free(conn->ctx, conn);
165 0 : return NULL;
166 : }
167 8 : tls_clear_password_cache(conn);
168 8 : conn->password_retries = 1;
169 :
170 16 : conn->parser =
171 8 : parser_new(conn->ctx, _handle_stream_start, _handle_stream_end,
172 : _handle_stream_stanza, conn);
173 : /* we own (and will free) the hash values */
174 8 : conn->id_handlers = hash_new(conn->ctx, 32, NULL);
175 :
176 : /* give the caller a reference to connection */
177 8 : conn->ref = 1;
178 :
179 : /* add connection to ctx->connlist */
180 8 : tail = conn->ctx->connlist;
181 8 : while (tail && tail->next)
182 : tail = tail->next;
183 :
184 8 : item = strophe_alloc(conn->ctx, sizeof(xmpp_connlist_t));
185 8 : if (!item) {
186 0 : strophe_error(conn->ctx, "xmpp", "failed to allocate memory");
187 0 : strophe_free(conn->ctx, conn->lang);
188 0 : parser_free(conn->parser);
189 0 : strophe_free(conn->ctx, conn);
190 0 : conn = NULL;
191 : } else {
192 8 : item->conn = conn;
193 8 : item->next = NULL;
194 :
195 8 : if (tail)
196 0 : tail->next = item;
197 : else
198 8 : conn->ctx->connlist = item;
199 : }
200 : }
201 :
202 : return conn;
203 : }
204 :
205 : /** Clone a Strophe connection object.
206 : *
207 : * @param conn a Strophe connection object
208 : *
209 : * @return the same conn object passed in with its reference count
210 : * incremented by 1
211 : *
212 : * @ingroup Connections
213 : */
214 0 : xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *conn)
215 : {
216 0 : conn->ref++;
217 0 : return conn;
218 : }
219 :
220 : /** Register sockopt callback
221 : * Set function to be called when a new socket is created to allow setting
222 : * socket options before connection is started.
223 : *
224 : * If the connection is already connected, this callback will be called
225 : * immediately.
226 : *
227 : * To set options that can only be applied to disconnected sockets, the
228 : * callback must be registered before connecting.
229 : *
230 : * @param conn The Strophe connection object this callback is being registered
231 : * for
232 : * @param callback a xmpp_sockopt_callback callback function that will receive
233 : * notifications of connection status
234 : *
235 : * @ingroup Connections
236 : */
237 :
238 0 : void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
239 : xmpp_sockopt_callback callback)
240 : {
241 0 : conn->sockopt_cb = callback;
242 0 : if (conn->state != XMPP_STATE_DISCONNECTED)
243 0 : callback(conn, &conn->sock);
244 0 : }
245 :
246 : /** Release a Strophe connection object.
247 : * Decrement the reference count by one for a connection, freeing the
248 : * connection object if the count reaches 0.
249 : *
250 : * @param conn a Strophe connection object
251 : *
252 : * @return TRUE if the connection object was freed and FALSE otherwise
253 : *
254 : * @ingroup Connections
255 : */
256 8 : int xmpp_conn_release(xmpp_conn_t *conn)
257 : {
258 8 : xmpp_ctx_t *ctx;
259 8 : xmpp_connlist_t *item, *prev;
260 8 : xmpp_handlist_t *hlitem, *thli;
261 8 : hash_iterator_t *iter;
262 8 : const char *key;
263 8 : int released = 0;
264 :
265 8 : if (conn->ref > 1)
266 0 : conn->ref--;
267 : else {
268 8 : ctx = conn->ctx;
269 :
270 8 : if (conn->state == XMPP_STATE_CONNECTING ||
271 : conn->state == XMPP_STATE_CONNECTED) {
272 0 : conn_disconnect(conn);
273 : }
274 :
275 : /* remove connection from context's connlist */
276 8 : if (ctx->connlist->conn == conn) {
277 8 : item = ctx->connlist;
278 8 : ctx->connlist = item->next;
279 8 : strophe_free(ctx, item);
280 : } else {
281 : prev = NULL;
282 : item = ctx->connlist;
283 0 : while (item && item->conn != conn) {
284 0 : prev = item;
285 0 : item = item->next;
286 : }
287 :
288 0 : if (!item) {
289 0 : strophe_error(ctx, "xmpp",
290 : "Connection not in context's list\n");
291 : } else {
292 0 : prev->next = item->next;
293 0 : strophe_free(ctx, item);
294 : }
295 : }
296 :
297 8 : _conn_reset(conn);
298 :
299 : /* free handler stuff
300 : * note that userdata is the responsibility of the client
301 : * and the handler pointers don't need to be freed since they
302 : * are pointers to functions */
303 :
304 8 : hlitem = conn->timed_handlers;
305 8 : while (hlitem) {
306 0 : thli = hlitem;
307 0 : hlitem = hlitem->next;
308 :
309 0 : strophe_free(ctx, thli);
310 : }
311 :
312 : /* id handlers
313 : * we have to traverse the hash table freeing list elements
314 : * then release the hash table */
315 8 : iter = hash_iter_new(conn->id_handlers);
316 8 : while ((key = hash_iter_next(iter))) {
317 0 : hlitem = (xmpp_handlist_t *)hash_get(conn->id_handlers, key);
318 0 : while (hlitem) {
319 0 : thli = hlitem;
320 0 : hlitem = hlitem->next;
321 0 : strophe_free(conn->ctx, thli->u.id);
322 0 : strophe_free(conn->ctx, thli);
323 : }
324 : }
325 8 : hash_iter_release(iter);
326 8 : hash_release(conn->id_handlers);
327 :
328 8 : hlitem = conn->handlers;
329 8 : while (hlitem) {
330 0 : thli = hlitem;
331 0 : hlitem = hlitem->next;
332 :
333 0 : if (thli->u.ns)
334 0 : strophe_free(ctx, thli->u.ns);
335 0 : if (thli->u.name)
336 0 : strophe_free(ctx, thli->u.name);
337 0 : if (thli->u.type)
338 0 : strophe_free(ctx, thli->u.type);
339 0 : strophe_free(ctx, thli);
340 : }
341 :
342 8 : parser_free(conn->parser);
343 :
344 8 : if (conn->jid)
345 0 : strophe_free(ctx, conn->jid);
346 8 : if (conn->pass)
347 0 : strophe_free(ctx, conn->pass);
348 8 : if (conn->lang)
349 8 : strophe_free(ctx, conn->lang);
350 8 : if (conn->tls_client_cert)
351 8 : strophe_free(ctx, conn->tls_client_cert);
352 8 : if (conn->tls_client_key)
353 2 : strophe_free(ctx, conn->tls_client_key);
354 8 : if (conn->tls_cafile)
355 0 : strophe_free(ctx, conn->tls_cafile);
356 8 : if (conn->tls_capath)
357 0 : strophe_free(ctx, conn->tls_capath);
358 8 : if (conn->sm_state)
359 0 : xmpp_free_sm_state(conn->sm_state);
360 8 : tls_clear_password_cache(conn);
361 8 : sock_free(conn->xsock);
362 8 : strophe_free(ctx, conn);
363 8 : released = 1;
364 : }
365 :
366 8 : return released;
367 : }
368 :
369 : /** Get the JID which is or will be bound to the connection.
370 : *
371 : * @param conn a Strophe connection object
372 : *
373 : * @return a string containing the full JID or NULL if it has not been set
374 : *
375 : * @ingroup Connections
376 : */
377 0 : const char *xmpp_conn_get_jid(const xmpp_conn_t *conn)
378 : {
379 0 : return conn->jid;
380 : }
381 :
382 : /**
383 : * Get the JID discovered during binding time.
384 : *
385 : * This JID will contain the resource used by the current connection.
386 : * This is useful in the case where a resource was not specified for
387 : * binding.
388 : *
389 : * @param conn a Strophe connection object.
390 : *
391 : * @return a string containing the full JID or NULL if it's not been discovered
392 : *
393 : * @ingroup Connections
394 : */
395 0 : const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *conn)
396 : {
397 0 : return conn->bound_jid;
398 : }
399 :
400 : /** Set the JID of the user that will be bound to the connection.
401 : * If any JID was previously set, it will be discarded. This should not be
402 : * be used after a connection is created. The function will make a copy of
403 : * the JID string. If the supplied JID is missing the node, SASL
404 : * ANONYMOUS authentication will be used.
405 : *
406 : * @param conn a Strophe connection object
407 : * @param jid a full or bare JID
408 : *
409 : * @ingroup Connections
410 : */
411 0 : void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid)
412 : {
413 0 : if (conn->jid)
414 0 : strophe_free(conn->ctx, conn->jid);
415 0 : conn->jid = strophe_strdup(conn->ctx, jid);
416 0 : }
417 :
418 : /** Set the Handler function which will be called when the TLS stack can't
419 : * verify the CA of the server we're trying to connect to.
420 : *
421 : * @param conn a Strophe connection object
422 : * @param hndl certfail Handler function
423 : *
424 : * @ingroup TLS
425 : */
426 0 : void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
427 : xmpp_certfail_handler hndl)
428 : {
429 0 : conn->certfail_handler = hndl;
430 0 : }
431 :
432 : /** Set the CAfile
433 : *
434 : * @param conn a Strophe connection object
435 : * @param path path to a certificate file
436 : *
437 : * @ingroup TLS
438 : */
439 0 : void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path)
440 : {
441 0 : if (conn->tls_cafile)
442 0 : strophe_free(conn->ctx, conn->tls_cafile);
443 0 : conn->tls_cafile = strophe_strdup(conn->ctx, path);
444 0 : }
445 :
446 : /** Set the CApath
447 : *
448 : * @param conn a Strophe connection object
449 : * @param path path to a folder containing certificates
450 : *
451 : * @ingroup TLS
452 : */
453 0 : void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path)
454 : {
455 0 : if (conn->tls_capath)
456 0 : strophe_free(conn->ctx, conn->tls_capath);
457 0 : conn->tls_capath = strophe_strdup(conn->ctx, path);
458 0 : }
459 :
460 : /** Retrieve the peer certificate
461 : *
462 : * The returned Certificate object must be free'd by calling
463 : * \ref xmpp_tlscert_free
464 : *
465 : * @param conn a Strophe connection object
466 : *
467 : * @return a Strophe Certificate object
468 : *
469 : * @ingroup TLS
470 : */
471 0 : xmpp_tlscert_t *xmpp_conn_get_peer_cert(xmpp_conn_t *const conn)
472 : {
473 0 : return tls_peer_cert(conn);
474 : }
475 :
476 : /** Set the Callback function which will be called when the TLS stack can't
477 : * decrypt a password protected key file.
478 : *
479 : * @param conn a Strophe connection object
480 : * @param cb The callback function that shall be called
481 : * @param userdata An opaque data pointer that will be passed to the callback
482 : *
483 : * @ingroup TLS
484 : */
485 3 : void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
486 : xmpp_password_callback cb,
487 : void *userdata)
488 : {
489 3 : conn->password_callback = cb;
490 3 : conn->password_callback_userdata = userdata;
491 3 : }
492 :
493 : /** Set the number of retry attempts to decrypt a private key file.
494 : *
495 : * In case the user enters the password manually it can be useful to
496 : * directly retry if the decryption of the key file failed.
497 : *
498 : * @param conn a Strophe connection object
499 : * @param retries The number of retries that should be tried
500 : *
501 : * @ingroup TLS
502 : */
503 0 : void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries)
504 : {
505 0 : if (retries == 0)
506 0 : conn->password_retries = 1;
507 : else
508 0 : conn->password_retries = retries;
509 0 : }
510 :
511 : /** Retrieve the path of the key file that shall be unlocked.
512 : *
513 : * This makes usually sense to be called from the
514 : * \ref xmpp_password_callback .
515 : *
516 : * @param conn a Strophe connection object
517 : *
518 : * @return a String of the path to the key file
519 : *
520 : * @ingroup TLS
521 : */
522 0 : const char *xmpp_conn_get_keyfile(const xmpp_conn_t *conn)
523 : {
524 0 : return conn->tls_client_key;
525 : }
526 :
527 : /** Set the Client Certificate and Private Key or PKCS#12 encoded file that
528 : * will be bound to the connection. If any of them was previously set, it
529 : * will be discarded. This should not be used after a connection is created.
530 : * The function will make a copy of the strings passed in.
531 : *
532 : * In case the Private Key is encrypted, a callback must be set via
533 : * \ref xmpp_conn_set_password_callback so the TLS stack can retrieve the
534 : * password.
535 : *
536 : * In case one wants to use a PKCS#12 encoded file, it should be passed via
537 : * the `cert` parameter and `key` should be NULL. Passing a PKCS#12 file in
538 : * `key` is deprecated.
539 : *
540 : * @param conn a Strophe connection object
541 : * @param cert path to a certificate file or a P12 file
542 : * @param key path to a private key file or a P12 file
543 : *
544 : * @ingroup TLS
545 : */
546 8 : void xmpp_conn_set_client_cert(xmpp_conn_t *const conn,
547 : const char *const cert,
548 : const char *const key)
549 : {
550 8 : strophe_debug(conn->ctx, "conn", "set client cert %s %s", cert, key);
551 8 : if (conn->tls_client_cert)
552 0 : strophe_free(conn->ctx, conn->tls_client_cert);
553 8 : conn->tls_client_cert = NULL;
554 8 : if (conn->tls_client_key)
555 0 : strophe_free(conn->ctx, conn->tls_client_key);
556 8 : conn->tls_client_key = NULL;
557 8 : if (cert && key) {
558 2 : conn->tls_client_cert = strophe_strdup(conn->ctx, cert);
559 2 : conn->tls_client_key = strophe_strdup(conn->ctx, key);
560 6 : } else if (cert && !key) {
561 3 : conn->tls_client_cert = strophe_strdup(conn->ctx, cert);
562 3 : } else if (!cert && key) {
563 3 : strophe_warn(conn->ctx, "xmpp",
564 : "xmpp_conn_set_client_cert: Passing PKCS#12 in 'key' "
565 : "parameter is deprecated. Use 'cert' instead");
566 3 : conn->tls_client_cert = strophe_strdup(conn->ctx, key);
567 : }
568 8 : }
569 :
570 : /** Get the number of xmppAddr entries in the client certificate.
571 : *
572 : * @param conn a Strophe connection object
573 : *
574 : * @return the number of xmppAddr entries in the client certificate
575 : *
576 : * @ingroup TLS
577 : */
578 8 : unsigned int xmpp_conn_cert_xmppaddr_num(xmpp_conn_t *const conn)
579 : {
580 8 : return tls_id_on_xmppaddr_num(conn);
581 : }
582 :
583 : /** Get a specific xmppAddr entry.
584 : *
585 : * @param conn a Strophe connection object
586 : * @param n the index of the entry, starting at 0
587 : *
588 : * @return a string containing the xmppAddr or NULL if n is out of range
589 : *
590 : * @ingroup TLS
591 : */
592 24 : char *xmpp_conn_cert_xmppaddr(xmpp_conn_t *const conn, unsigned int n)
593 : {
594 24 : return tls_id_on_xmppaddr(conn, n);
595 : }
596 :
597 : /** Get the password used for authentication of a connection.
598 : *
599 : * @param conn a Strophe connection object
600 : *
601 : * @return a string containing the password or NULL if it has not been set
602 : *
603 : * @ingroup Connections
604 : */
605 0 : const char *xmpp_conn_get_pass(const xmpp_conn_t *conn)
606 : {
607 0 : return conn->pass;
608 : }
609 :
610 : /** Set the password used to authenticate the connection.
611 : * If any password was previously set, it will be discarded. The function
612 : * will make a copy of the password string.
613 : *
614 : * @param conn a Strophe connection object
615 : * @param pass the password
616 : *
617 : * @ingroup Connections
618 : */
619 0 : void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass)
620 : {
621 0 : if (conn->pass)
622 0 : strophe_free(conn->ctx, conn->pass);
623 0 : conn->pass = pass ? strophe_strdup(conn->ctx, pass) : NULL;
624 0 : }
625 :
626 : /** Get the strophe context that the connection is associated with.
627 : * @param conn a Strophe connection object
628 : *
629 : * @return a Strophe context
630 : *
631 : * @ingroup Connections
632 : */
633 0 : xmpp_ctx_t *xmpp_conn_get_context(xmpp_conn_t *conn)
634 : {
635 0 : return conn->ctx;
636 : }
637 :
638 : /** Initiate a connection to the XMPP server.
639 : * This function returns immediately after starting the connection
640 : * process to the XMPP server, and notifications of connection state changes
641 : * will be sent to the callback function. The domain and port to connect to
642 : * are usually determined by an SRV lookup for the xmpp-client service at
643 : * the domain specified in the JID. If SRV lookup fails, altdomain and
644 : * altport will be used instead if specified.
645 : *
646 : * @param conn a Strophe connection object
647 : * @param altdomain a string with domain to use if SRV lookup fails. If this
648 : * is NULL, the domain from the JID will be used.
649 : * @param altport an integer port number to use if SRV lookup fails. If this
650 : * is 0, the default port will be assumed.
651 : * @param callback a xmpp_conn_handler callback function that will receive
652 : * notifications of connection status
653 : * @param userdata an opaque data pointer that will be passed to the callback
654 : *
655 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
656 : *
657 : * @ingroup Connections
658 : */
659 0 : int xmpp_connect_client(xmpp_conn_t *conn,
660 : const char *altdomain,
661 : unsigned short altport,
662 : xmpp_conn_handler callback,
663 : void *userdata)
664 : {
665 0 : char *domain;
666 0 : int rc;
667 :
668 0 : if (!conn->jid && (conn->tls_client_cert || conn->tls_client_key)) {
669 0 : if (tls_id_on_xmppaddr_num(conn) != 1) {
670 0 : strophe_debug(conn->ctx, "xmpp",
671 : "Client certificate contains multiple or no xmppAddr "
672 : "and no JID was given to be used.");
673 0 : return XMPP_EINVOP;
674 : }
675 0 : conn->jid = tls_id_on_xmppaddr(conn, 0);
676 0 : if (!conn->jid)
677 : return XMPP_EMEM;
678 0 : strophe_debug(conn->ctx, "xmpp", "Use jid %s from id-on-xmppAddr.",
679 : conn->jid);
680 : }
681 :
682 0 : if (!conn->jid) {
683 0 : strophe_error(conn->ctx, "xmpp", "JID is not set.");
684 0 : return XMPP_EINVOP;
685 : }
686 :
687 0 : domain = xmpp_jid_domain(conn->ctx, conn->jid);
688 0 : if (!domain)
689 : return XMPP_EMEM;
690 :
691 0 : if (!conn->sm_state) {
692 0 : conn->sm_state = strophe_alloc(conn->ctx, sizeof(*conn->sm_state));
693 0 : if (!conn->sm_state)
694 0 : goto err_mem;
695 0 : memset(conn->sm_state, 0, sizeof(*conn->sm_state));
696 0 : conn->sm_state->ctx = conn->ctx;
697 : }
698 :
699 0 : if (altdomain != NULL)
700 0 : strophe_debug(conn->ctx, "conn", "Connecting via altdomain.");
701 :
702 0 : if (conn->tls_legacy_ssl && !altdomain) {
703 : /* SSL tunneled connection on 5223 port is legacy and doesn't
704 : * have an SRV record. */
705 0 : altdomain = domain;
706 : }
707 0 : altport = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
708 :
709 0 : if (conn->xsock)
710 0 : sock_free(conn->xsock);
711 0 : conn->xsock = sock_new(conn, domain, altdomain, altport);
712 0 : if (!conn->xsock)
713 0 : goto err_mem;
714 :
715 0 : rc = _conn_connect(conn, domain, XMPP_CLIENT, callback, userdata);
716 0 : strophe_free(conn->ctx, domain);
717 :
718 0 : return rc;
719 :
720 0 : err_mem:
721 0 : strophe_free(conn->ctx, domain);
722 0 : return XMPP_EMEM;
723 : }
724 :
725 : /** Initiate a component connection to server.
726 : * This function returns immediately after starting the connection
727 : * process to the XMPP server, and notifications of connection state changes
728 : * will be sent to the internal callback function that will set up handler
729 : * for the component handshake as defined in XEP-0114.
730 : * The domain and port to connect to must be provided in this case as the JID
731 : * provided to the call serves as component identifier to the server and is
732 : * not subject to DNS resolution.
733 : *
734 : * @param conn a Strophe connection object
735 : * @param server a string with domain to use directly as the domain can't be
736 : * extracted from the component name/JID. If this is not set, the call
737 : * will fail.
738 : * @param port an integer port number to use to connect to server expecting
739 : * an external component. If this is 0, the port 5347 will be assumed.
740 : * @param callback a xmpp_conn_handler callback function that will receive
741 : * notifications of connection status
742 : * @param userdata an opaque data pointer that will be passed to the callback
743 : *
744 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
745 : *
746 : * @ingroup Connections
747 : */
748 0 : int xmpp_connect_component(xmpp_conn_t *conn,
749 : const char *server,
750 : unsigned short port,
751 : xmpp_conn_handler callback,
752 : void *userdata)
753 : {
754 : /* The server domain, jid and password MUST be specified. */
755 0 : if (!(server && conn->jid && conn->pass))
756 : return XMPP_EINVOP;
757 :
758 : /* XEP-0114 does not support TLS */
759 0 : (void)xmpp_conn_set_flags(conn, xmpp_conn_get_flags(conn) |
760 : XMPP_CONN_FLAG_DISABLE_TLS);
761 0 : if (!conn->tls_disabled) {
762 0 : strophe_error(conn->ctx, "conn",
763 : "Failed to disable TLS. "
764 : "XEP-0114 does not support TLS");
765 0 : return XMPP_EINT;
766 : }
767 :
768 0 : port = port ? port : _conn_default_port(conn, XMPP_COMPONENT);
769 0 : if (conn->xsock)
770 0 : sock_free(conn->xsock);
771 0 : conn->xsock = sock_new(conn, NULL, server, port);
772 0 : if (!conn->xsock)
773 : return XMPP_EMEM;
774 :
775 : /* JID serves as an identifier here and will be used as "to" attribute
776 : of the stream */
777 0 : return _conn_connect(conn, conn->jid, XMPP_COMPONENT, callback, userdata);
778 : }
779 :
780 : /** Initiate a raw connection to the XMPP server.
781 : * Arguments and behaviour of the function are similar to
782 : * xmpp_connect_client(), but it skips authentication process. In opposite to
783 : * xmpp_connect_client() during connection process two events are generated
784 : * instead of one. User's callback is called with event XMPP_CONN_RAW_CONNECT
785 : * when the TCP connection with the server is established. At this point user
786 : * might want to open an XMPP stream with xmpp_conn_open_stream() or establish
787 : * TLS session with xmpp_conn_tls_start(). Event XMPP_CONN_CONNECT is generated
788 : * when the XMPP stream is opened successfully and user may send stanzas over
789 : * the connection.
790 : *
791 : * This function doesn't use password nor node part of a jid. Therefore,
792 : * the only required configuration is a domain (or full jid) passed via
793 : * xmpp_conn_set_jid().
794 : *
795 : * @see xmpp_connect_client()
796 : *
797 : * @return XMPP_EOK (0) on success a number less than 0 on failure
798 : *
799 : * @ingroup Connections
800 : */
801 0 : int xmpp_connect_raw(xmpp_conn_t *conn,
802 : const char *altdomain,
803 : unsigned short altport,
804 : xmpp_conn_handler callback,
805 : void *userdata)
806 : {
807 0 : conn->is_raw = 1;
808 0 : return xmpp_connect_client(conn, altdomain, altport, callback, userdata);
809 : }
810 :
811 : /* Called when tcp connection is established. */
812 0 : void conn_established(xmpp_conn_t *conn)
813 : {
814 0 : if (conn->tls_legacy_ssl && !conn->is_raw) {
815 0 : strophe_debug(conn->ctx, "xmpp", "using legacy SSL connection");
816 0 : if (conn_tls_start(conn) != 0) {
817 0 : conn_disconnect(conn);
818 0 : return;
819 : }
820 : }
821 :
822 0 : if (conn->is_raw) {
823 0 : handler_reset_timed(conn, 0);
824 : /* we skip all the mandatory steps of the stream negotiation for a "raw"
825 : connection, but the event loop ignores user's handlers when
826 : conn->stream_negotiation_completed is not set. */
827 0 : conn->stream_negotiation_completed = 1;
828 0 : conn->conn_handler(conn, XMPP_CONN_RAW_CONNECT, 0, NULL,
829 : conn->userdata);
830 : } else {
831 : /* send stream init */
832 0 : conn_open_stream(conn);
833 : }
834 : }
835 :
836 : /** Send the default opening stream tag.
837 : * The default tag is the one sent by xmpp_connect_client().
838 : * User's connection handler is called with event XMPP_CONN_CONNECT when
839 : * server replies with its opening tag.
840 : *
841 : * @return XMPP_EOK (0) on success a number less than 0 on failure
842 : *
843 : * @note The connection must be connected with xmpp_connect_raw().
844 : *
845 : * @ingroup Connections
846 : */
847 0 : int xmpp_conn_open_stream_default(xmpp_conn_t *conn)
848 : {
849 0 : if (!conn->is_raw)
850 : return XMPP_EINVOP;
851 :
852 0 : conn_prepare_reset(conn, auth_handle_open_raw);
853 0 : conn_open_stream(conn);
854 :
855 0 : return XMPP_EOK;
856 : }
857 :
858 : /** Send an opening stream tag.
859 : * User's connection handler is called with event XMPP_CONN_CONNECT when
860 : * server replies with its opening tag.
861 : *
862 : * @param conn a Strophe connection object
863 : * @param attributes Array of strings in format: even index points to
864 : * an attribute name and odd index points to its value
865 : * @param attributes_len Number of elements in the attributes array, it
866 : * should be number of attributes multiplied by 2
867 : *
868 : * @return XMPP_EOK (0) on success a number less than 0 on failure
869 : *
870 : * @note The connection must be connected with xmpp_connect_raw().
871 : *
872 : * @ingroup Connections
873 : */
874 0 : int xmpp_conn_open_stream(xmpp_conn_t *conn,
875 : char **attributes,
876 : size_t attributes_len)
877 : {
878 0 : if (!conn->is_raw)
879 : return XMPP_EINVOP;
880 :
881 0 : conn_prepare_reset(conn, auth_handle_open_raw);
882 :
883 0 : return _conn_open_stream_with_attributes(conn, attributes, attributes_len);
884 : }
885 :
886 : /** Start synchronous TLS handshake with the server.
887 : *
888 : * @return XMPP_EOK (0) on success a number less than 0 on failure
889 : *
890 : * @ingroup Connections
891 : */
892 0 : int xmpp_conn_tls_start(xmpp_conn_t *conn)
893 : {
894 0 : return conn_tls_start(conn);
895 : }
896 :
897 : /** Cleanly disconnect the connection.
898 : * This function is only called by the stream parser when </stream:stream>
899 : * is received, and it not intended to be called by code outside of Strophe.
900 : *
901 : * @param conn a Strophe connection object
902 : */
903 0 : void conn_disconnect_clean(xmpp_conn_t *conn)
904 : {
905 : /* remove the timed handler */
906 0 : xmpp_timed_handler_delete(conn, _disconnect_cleanup);
907 :
908 0 : conn_disconnect(conn);
909 0 : }
910 :
911 : /** Disconnect from the XMPP server.
912 : * This function immediately disconnects from the XMPP server, and should
913 : * not be used outside of the Strophe library.
914 : *
915 : * @param conn a Strophe connection object
916 : */
917 0 : void conn_disconnect(xmpp_conn_t *conn)
918 : {
919 0 : strophe_debug(conn->ctx, "xmpp", "Closing socket.");
920 0 : conn->state = XMPP_STATE_DISCONNECTED;
921 0 : conn->stream_negotiation_completed = 0;
922 0 : if (conn->tls) {
923 0 : tls_stop(conn->tls);
924 0 : tls_free(conn->tls);
925 0 : conn->tls = NULL;
926 : }
927 0 : if (conn->sock != INVALID_SOCKET)
928 0 : sock_close(conn->sock);
929 0 : _reset_sm_state_for_reconnect(conn);
930 :
931 : /* fire off connection handler */
932 0 : conn->conn_handler(conn, XMPP_CONN_DISCONNECT, conn->error,
933 : conn->stream_error, conn->userdata);
934 0 : }
935 :
936 : /* prepares a parser reset. this is called from handlers. we can't
937 : * reset the parser immediately as it is not re-entrant. */
938 0 : void conn_prepare_reset(xmpp_conn_t *conn, xmpp_open_handler handler)
939 : {
940 0 : conn->reset_parser = 1;
941 0 : conn->open_handler = handler;
942 0 : }
943 :
944 : /* reset the parser */
945 0 : void conn_parser_reset(xmpp_conn_t *conn)
946 : {
947 0 : conn->reset_parser = 0;
948 0 : parser_reset(conn->parser);
949 0 : }
950 :
951 : /** Initiate termination of the connection to the XMPP server.
952 : * This function starts the disconnection sequence by sending
953 : * </stream:stream> to the XMPP server. This function will do nothing
954 : * if the connection state is different from CONNECTING or CONNECTED.
955 : *
956 : * @param conn a Strophe connection object
957 : *
958 : * @ingroup Connections
959 : */
960 0 : void xmpp_disconnect(xmpp_conn_t *conn)
961 : {
962 0 : if (conn->state != XMPP_STATE_CONNECTING &&
963 : conn->state != XMPP_STATE_CONNECTED)
964 : return;
965 :
966 : /* close the stream */
967 0 : send_raw_string(conn, "</stream:stream>");
968 :
969 : /* setup timed handler in case disconnect takes too long */
970 0 : handler_add_timed(conn, _disconnect_cleanup, DISCONNECT_TIMEOUT, NULL);
971 : }
972 :
973 : /** Send a raw string to the XMPP server.
974 : * This function is a convenience function to send raw string data to the
975 : * XMPP server. It is used by Strophe to send short messages instead of
976 : * building up an XML stanza with DOM methods. This should be used with care
977 : * as it does not validate the data; invalid data may result in immediate
978 : * stream termination by the XMPP server.
979 : *
980 : * @param conn a Strophe connection object
981 : * @param fmt a printf-style format string followed by a variable list of
982 : * arguments to format
983 : *
984 : * @ingroup Connections
985 : */
986 0 : void xmpp_send_raw_string(xmpp_conn_t *conn, const char *fmt, ...)
987 : {
988 0 : va_list ap;
989 :
990 0 : if (!_is_connected(conn, XMPP_QUEUE_USER))
991 0 : return;
992 :
993 0 : va_start(ap, fmt);
994 0 : _send_valist(conn, fmt, ap, XMPP_QUEUE_USER);
995 0 : va_end(ap);
996 : }
997 :
998 : /** Send raw bytes to the XMPP server.
999 : * This function is a convenience function to send raw bytes to the
1000 : * XMPP server. It is used primarily by xmpp_send_raw_string(). This
1001 : * function should be used with care as it does not validate the bytes and
1002 : * invalid data may result in stream termination by the XMPP server.
1003 : *
1004 : * @param conn a Strophe connection object
1005 : * @param data a buffer of raw bytes
1006 : * @param len the length of the data in the buffer
1007 : *
1008 : * @ingroup Connections
1009 : */
1010 0 : void xmpp_send_raw(xmpp_conn_t *conn, const char *data, size_t len)
1011 : {
1012 0 : send_raw(conn, data, len, XMPP_QUEUE_USER, NULL);
1013 0 : }
1014 :
1015 : /** Send an XML stanza to the XMPP server.
1016 : * This is the main way to send data to the XMPP server. The function will
1017 : * terminate without action if the connection state is not CONNECTED.
1018 : *
1019 : * @param conn a Strophe connection object
1020 : * @param stanza a Strophe stanza object
1021 : *
1022 : * @ingroup Connections
1023 : */
1024 0 : void xmpp_send(xmpp_conn_t *conn, xmpp_stanza_t *stanza)
1025 : {
1026 0 : send_stanza(conn, xmpp_stanza_clone(stanza), XMPP_QUEUE_USER);
1027 0 : }
1028 :
1029 : /** Send the opening <stream:stream> tag to the server.
1030 : * This function is used by Strophe to begin an XMPP stream. It should
1031 : * not be used outside of the library.
1032 : *
1033 : * @param conn a Strophe connection object
1034 : */
1035 0 : void conn_open_stream(xmpp_conn_t *conn)
1036 : {
1037 0 : size_t attributes_len;
1038 0 : int rc;
1039 0 : char *from = NULL;
1040 0 : char *ns = conn->type == XMPP_CLIENT ? XMPP_NS_CLIENT : XMPP_NS_COMPONENT;
1041 0 : char *attributes[12] = {
1042 0 : "to", conn->domain, "xml:lang", conn->lang,
1043 : "version", "1.0", "xmlns", ns,
1044 : "xmlns:stream", XMPP_NS_STREAMS, "from", NULL};
1045 :
1046 0 : attributes_len = ARRAY_SIZE(attributes);
1047 0 : if (conn->tls && conn->jid && strchr(conn->jid, '@') != NULL)
1048 0 : from = xmpp_jid_bare(conn->ctx, conn->jid);
1049 :
1050 0 : if (from)
1051 0 : attributes[attributes_len - 1] = from;
1052 : else
1053 : attributes_len -= 2;
1054 :
1055 0 : rc = _conn_open_stream_with_attributes(conn, attributes, attributes_len);
1056 0 : if (rc != XMPP_EOK) {
1057 0 : strophe_error(conn->ctx, "conn",
1058 : "Cannot build stream tag: memory error");
1059 0 : conn_disconnect(conn);
1060 : }
1061 0 : if (from)
1062 0 : strophe_free(conn->ctx, from);
1063 0 : }
1064 :
1065 0 : int conn_interface_write(struct conn_interface *intf,
1066 : const void *buff,
1067 : size_t len)
1068 : {
1069 0 : int ret = intf->write(intf, buff, len);
1070 0 : if (ret < 0 && !intf->error_is_recoverable(intf, intf->get_error(intf))) {
1071 0 : intf->conn->error = intf->get_error(intf);
1072 : }
1073 0 : return ret;
1074 : }
1075 :
1076 0 : int conn_int_nop(struct conn_interface *intf)
1077 : {
1078 0 : UNUSED(intf);
1079 0 : return 0;
1080 : }
1081 :
1082 0 : int conn_tls_start(xmpp_conn_t *conn)
1083 : {
1084 0 : int rc;
1085 :
1086 0 : if (conn->tls_disabled) {
1087 0 : conn->tls = NULL;
1088 0 : rc = XMPP_EINVOP;
1089 : } else {
1090 0 : conn->tls = tls_new(conn);
1091 0 : rc = conn->tls == NULL ? XMPP_EMEM : 0;
1092 : }
1093 :
1094 0 : if (conn->tls != NULL) {
1095 0 : struct conn_interface old_intf = conn->intf;
1096 0 : conn->intf = tls_intf;
1097 0 : conn->intf.conn = conn;
1098 0 : if (tls_start(conn->tls)) {
1099 0 : conn->secured = 1;
1100 : } else {
1101 0 : rc = XMPP_EINT;
1102 0 : conn->error = tls_error(&conn->intf);
1103 0 : tls_free(conn->tls);
1104 0 : conn->tls = NULL;
1105 0 : conn->tls_failed = 1;
1106 0 : conn->intf = old_intf;
1107 : }
1108 : }
1109 0 : if (rc != 0) {
1110 0 : strophe_debug(conn->ctx, "conn",
1111 : "Couldn't start TLS! "
1112 : "error %d tls_error %d",
1113 : rc, conn->error);
1114 : }
1115 0 : return rc;
1116 : }
1117 :
1118 : /** Return applied flags for the connection.
1119 : *
1120 : * @param conn a Strophe connection object
1121 : *
1122 : * @return ORed connection flags that are applied for the connection.
1123 : *
1124 : * @ingroup Connections
1125 : */
1126 0 : long xmpp_conn_get_flags(const xmpp_conn_t *conn)
1127 : {
1128 0 : long flags;
1129 :
1130 0 : flags =
1131 0 : XMPP_CONN_FLAG_DISABLE_TLS * conn->tls_disabled |
1132 0 : XMPP_CONN_FLAG_MANDATORY_TLS * conn->tls_mandatory |
1133 0 : XMPP_CONN_FLAG_LEGACY_SSL * conn->tls_legacy_ssl |
1134 0 : XMPP_CONN_FLAG_TRUST_TLS * conn->tls_trust |
1135 0 : XMPP_CONN_FLAG_DISABLE_SM * conn->sm_disable |
1136 0 : XMPP_CONN_FLAG_ENABLE_COMPRESSION * conn->compression.allowed |
1137 0 : XMPP_CONN_FLAG_COMPRESSION_DONT_RESET * conn->compression.dont_reset |
1138 0 : XMPP_CONN_FLAG_LEGACY_AUTH * conn->auth_legacy_enabled;
1139 :
1140 0 : return flags;
1141 : }
1142 :
1143 : /** Set flags for the connection.
1144 : * This function applies set flags and resets unset ones. Default connection
1145 : * configuration is all flags unset. Flags can be applied only for a connection
1146 : * in disconnected state.
1147 : * All unsupported flags are ignored. If a flag is unset after successful set
1148 : * operation then the flag is not supported by current version.
1149 : *
1150 : * Supported flags are:
1151 : *
1152 : * - \ref XMPP_CONN_FLAG_DISABLE_TLS
1153 : * - \ref XMPP_CONN_FLAG_MANDATORY_TLS
1154 : * - \ref XMPP_CONN_FLAG_LEGACY_SSL
1155 : * - \ref XMPP_CONN_FLAG_TRUST_TLS
1156 : * - \ref XMPP_CONN_FLAG_LEGACY_AUTH
1157 : * - \ref XMPP_CONN_FLAG_DISABLE_SM
1158 : * - \ref XMPP_CONN_FLAG_ENABLE_COMPRESSION
1159 : * - \ref XMPP_CONN_FLAG_COMPRESSION_DONT_RESET
1160 : *
1161 : * @param conn a Strophe connection object
1162 : * @param flags ORed connection flags
1163 : *
1164 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
1165 : *
1166 : * @ingroup Connections
1167 : */
1168 0 : int xmpp_conn_set_flags(xmpp_conn_t *conn, long flags)
1169 : {
1170 0 : if (conn->state != XMPP_STATE_DISCONNECTED) {
1171 0 : strophe_error(conn->ctx, "conn",
1172 : "Flags can be set only "
1173 : "for disconnected connection");
1174 0 : return XMPP_EINVOP;
1175 : }
1176 0 : if ((flags & XMPP_CONN_FLAG_DISABLE_TLS) &&
1177 : (flags & (XMPP_CONN_FLAG_MANDATORY_TLS | XMPP_CONN_FLAG_LEGACY_SSL |
1178 : XMPP_CONN_FLAG_TRUST_TLS))) {
1179 0 : strophe_error(conn->ctx, "conn", "Flags 0x%04lx conflict", flags);
1180 0 : return XMPP_EINVOP;
1181 : }
1182 :
1183 0 : conn->tls_disabled = (flags & XMPP_CONN_FLAG_DISABLE_TLS) ? 1 : 0;
1184 0 : conn->tls_mandatory = (flags & XMPP_CONN_FLAG_MANDATORY_TLS) ? 1 : 0;
1185 0 : conn->tls_legacy_ssl = (flags & XMPP_CONN_FLAG_LEGACY_SSL) ? 1 : 0;
1186 0 : conn->tls_trust = (flags & XMPP_CONN_FLAG_TRUST_TLS) ? 1 : 0;
1187 0 : conn->auth_legacy_enabled = (flags & XMPP_CONN_FLAG_LEGACY_AUTH) ? 1 : 0;
1188 0 : conn->sm_disable = (flags & XMPP_CONN_FLAG_DISABLE_SM) ? 1 : 0;
1189 0 : conn->compression.allowed =
1190 0 : (flags & XMPP_CONN_FLAG_ENABLE_COMPRESSION) ? 1 : 0;
1191 0 : conn->compression.dont_reset =
1192 0 : (flags & XMPP_CONN_FLAG_COMPRESSION_DONT_RESET) ? 1 : 0;
1193 0 : flags &= ~(XMPP_CONN_FLAG_DISABLE_TLS | XMPP_CONN_FLAG_MANDATORY_TLS |
1194 : XMPP_CONN_FLAG_LEGACY_SSL | XMPP_CONN_FLAG_TRUST_TLS |
1195 : XMPP_CONN_FLAG_LEGACY_AUTH | XMPP_CONN_FLAG_DISABLE_SM |
1196 : XMPP_CONN_FLAG_ENABLE_COMPRESSION |
1197 : XMPP_CONN_FLAG_COMPRESSION_DONT_RESET);
1198 0 : if (flags) {
1199 0 : strophe_error(conn->ctx, "conn", "Flags 0x%04lx unknown", flags);
1200 0 : return XMPP_EINVOP;
1201 : }
1202 :
1203 : return 0;
1204 : }
1205 :
1206 : /** Return whether TLS session is established or not.
1207 : *
1208 : * @return TRUE if TLS session is established and FALSE otherwise
1209 : *
1210 : * @ingroup Connections
1211 : */
1212 0 : int xmpp_conn_is_secured(xmpp_conn_t *conn)
1213 : {
1214 0 : return conn->secured && !conn->tls_failed && conn->tls != NULL;
1215 : }
1216 :
1217 : /**
1218 : * @return TRUE if connection is in connecting state and FALSE otherwise
1219 : *
1220 : * @ingroup Connections
1221 : */
1222 0 : int xmpp_conn_is_connecting(xmpp_conn_t *conn)
1223 : {
1224 0 : return conn->state == XMPP_STATE_CONNECTING ||
1225 0 : (conn->state == XMPP_STATE_CONNECTED &&
1226 0 : conn->stream_negotiation_completed == 0);
1227 : }
1228 :
1229 0 : static int _is_connected(xmpp_conn_t *conn, xmpp_send_queue_owner_t owner)
1230 : {
1231 0 : return conn->state == XMPP_STATE_CONNECTED &&
1232 0 : (owner != XMPP_QUEUE_USER ||
1233 0 : conn->stream_negotiation_completed == 1);
1234 : }
1235 :
1236 : /**
1237 : * @return TRUE if connection is established and FALSE otherwise
1238 : *
1239 : * @ingroup Connections
1240 : */
1241 0 : int xmpp_conn_is_connected(xmpp_conn_t *conn)
1242 : {
1243 0 : return _is_connected(conn, XMPP_QUEUE_USER);
1244 : }
1245 :
1246 : /**
1247 : * @return TRUE if connection is in disconnected state and FALSE otherwise
1248 : *
1249 : * @ingroup Connections
1250 : */
1251 0 : int xmpp_conn_is_disconnected(xmpp_conn_t *conn)
1252 : {
1253 0 : return conn->state == XMPP_STATE_DISCONNECTED;
1254 : }
1255 :
1256 : /**
1257 : * This returns the Stream Management state of a connection object after
1258 : * it has been disconnected.
1259 : * One can then initialise a fresh connection object and set this Stream
1260 : * Management state by calling \ref xmpp_conn_set_sm_state
1261 : *
1262 : * In case one wants to dispose of the state w/o setting it into a fresh
1263 : * connection object, one can call \ref xmpp_free_sm_state
1264 : *
1265 : * After calling this function to retrieve the state, only call one of the
1266 : * other two.
1267 : *
1268 : * @param conn a Strophe connection object
1269 : * @return The Stream Management state of the connection or NULL on error
1270 : *
1271 : * @ingroup Connections
1272 : */
1273 0 : xmpp_sm_state_t *xmpp_conn_get_sm_state(xmpp_conn_t *conn)
1274 : {
1275 0 : xmpp_sm_state_t *ret;
1276 :
1277 : /* We can only return the SM state when we're disconnected */
1278 0 : if (conn->state != XMPP_STATE_DISCONNECTED)
1279 : return NULL;
1280 :
1281 0 : ret = conn->sm_state;
1282 0 : conn->sm_state = NULL;
1283 :
1284 0 : return ret;
1285 : }
1286 :
1287 0 : static void _reset_sm_state_for_reconnect(xmpp_conn_t *conn)
1288 : {
1289 0 : xmpp_sm_state_t *s = conn->sm_state;
1290 :
1291 0 : if (s->previd) {
1292 0 : strophe_free(conn->ctx, s->previd);
1293 0 : s->previd = NULL;
1294 : }
1295 :
1296 0 : if (s->can_resume) {
1297 0 : s->previd = s->id;
1298 0 : s->id = NULL;
1299 :
1300 0 : s->bound_jid = conn->bound_jid;
1301 0 : conn->bound_jid = NULL;
1302 0 : } else if (s->id) {
1303 0 : strophe_free(conn->ctx, s->id);
1304 0 : s->id = NULL;
1305 : }
1306 :
1307 0 : s->r_sent = s->sm_enabled = s->sm_support = s->resume = 0;
1308 :
1309 0 : if (s->bind) {
1310 0 : xmpp_stanza_release(s->bind);
1311 0 : s->bind = NULL;
1312 : }
1313 0 : }
1314 :
1315 : /**
1316 : * @param conn a Strophe connection object
1317 : * @param sm_state A Stream Management state returned from a call to
1318 : * `xmpp_conn_get_sm_state()`
1319 : *
1320 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
1321 : *
1322 : * @ingroup Connections
1323 : */
1324 0 : int xmpp_conn_set_sm_state(xmpp_conn_t *conn, xmpp_sm_state_t *sm_state)
1325 : {
1326 : /* We can only set the SM state when we're disconnected */
1327 0 : if (conn->state != XMPP_STATE_DISCONNECTED) {
1328 0 : strophe_error(conn->ctx, "conn",
1329 : "SM state can only be set the when we're disconnected");
1330 0 : return XMPP_EINVOP;
1331 : }
1332 :
1333 0 : if (conn->sm_state) {
1334 0 : strophe_error(conn->ctx, "conn", "SM state is already set!");
1335 0 : return XMPP_EINVOP;
1336 : }
1337 :
1338 0 : if (conn->ctx != sm_state->ctx) {
1339 0 : strophe_error(
1340 : conn->ctx, "conn",
1341 : "SM state has to be assigned to connection that stems from "
1342 : "the same context!");
1343 0 : return XMPP_EINVOP;
1344 : }
1345 :
1346 0 : conn->sm_state = sm_state;
1347 0 : return XMPP_EOK;
1348 : }
1349 :
1350 0 : void reset_sm_state(xmpp_sm_state_t *sm_state)
1351 : {
1352 0 : xmpp_ctx_t *ctx = sm_state->ctx;
1353 :
1354 0 : strophe_free_and_null(ctx, sm_state->id);
1355 0 : strophe_free_and_null(ctx, sm_state->previd);
1356 0 : strophe_free_and_null(ctx, sm_state->bound_jid);
1357 0 : if (sm_state->bind)
1358 0 : xmpp_stanza_release(sm_state->bind);
1359 0 : sm_state->bind = NULL;
1360 0 : sm_state->sm_handled_nr = 0;
1361 0 : sm_state->sm_sent_nr = 0;
1362 0 : sm_state->r_sent = 0;
1363 0 : }
1364 :
1365 : /** c.f. \ref xmpp_conn_get_sm_state for usage documentation
1366 : *
1367 : * @param sm_state A Stream Management state returned from a call to
1368 : * `xmpp_conn_get_sm_state()`
1369 : *
1370 : * @ingroup Connections
1371 : */
1372 0 : void xmpp_free_sm_state(xmpp_sm_state_t *sm_state)
1373 : {
1374 0 : xmpp_send_queue_t *smq;
1375 0 : xmpp_ctx_t *ctx;
1376 :
1377 0 : if (!sm_state || !sm_state->ctx)
1378 : return;
1379 :
1380 : ctx = sm_state->ctx;
1381 :
1382 0 : while ((smq = pop_queue_front(&sm_state->sm_queue))) {
1383 0 : strophe_free(ctx, queue_element_free(ctx, smq));
1384 : }
1385 :
1386 0 : reset_sm_state(sm_state);
1387 0 : strophe_free(ctx, sm_state);
1388 : }
1389 :
1390 : /**
1391 : * @return The number of entries in the send queue
1392 : *
1393 : * @ingroup Connections
1394 : */
1395 0 : int xmpp_conn_send_queue_len(const xmpp_conn_t *conn)
1396 : {
1397 0 : if (conn->send_queue_head && conn->send_queue_head->wip &&
1398 0 : conn->send_queue_head->owner == XMPP_QUEUE_USER)
1399 0 : return conn->send_queue_user_len - 1;
1400 : else
1401 0 : return conn->send_queue_user_len;
1402 : }
1403 :
1404 0 : static char *_drop_send_queue_element(xmpp_conn_t *conn, xmpp_send_queue_t *e)
1405 : {
1406 0 : if (e == conn->send_queue_head)
1407 0 : conn->send_queue_head = e->next;
1408 0 : if (e == conn->send_queue_tail)
1409 0 : conn->send_queue_tail = e->prev;
1410 0 : if (!conn->send_queue_head)
1411 0 : conn->send_queue_tail = NULL;
1412 0 : if (e->prev)
1413 0 : e->prev->next = e->next;
1414 0 : if (e->next)
1415 0 : e->next->prev = e->prev;
1416 0 : conn->send_queue_len--;
1417 0 : if (e->owner == XMPP_QUEUE_USER)
1418 0 : conn->send_queue_user_len--;
1419 0 : return queue_element_free(conn->ctx, e);
1420 : }
1421 :
1422 : /** Drop an element of the send queue.
1423 : * This can be used to manage the send queue in case a server
1424 : * isn't fast enough in processing the elements you're trying
1425 : * to send or your outgoing bandwidth isn't fast enough to transfer
1426 : * everything you want to send out.
1427 : *
1428 : * @param conn a Strophe connection object
1429 : * @param which the element that shall be removed
1430 : *
1431 : * @return The rendered stanza. The pointer returned has to be free'd by the
1432 : * caller of this function.
1433 : *
1434 : * @ingroup Connections
1435 : */
1436 0 : char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
1437 : xmpp_queue_element_t which)
1438 : {
1439 0 : xmpp_send_queue_t *t;
1440 0 : int disconnected = conn->state == XMPP_STATE_DISCONNECTED;
1441 :
1442 : /* Fast return paths */
1443 : /* empty queue */
1444 0 : if (!conn->send_queue_head)
1445 : return NULL;
1446 : /* one element in queue */
1447 0 : if (conn->send_queue_head == conn->send_queue_tail) {
1448 : /* head is already sent out partially */
1449 0 : if (conn->send_queue_head->wip && !disconnected)
1450 : return NULL;
1451 : /* the element is no USER element */
1452 0 : if (conn->send_queue_head->owner != XMPP_QUEUE_USER)
1453 : return NULL;
1454 : }
1455 :
1456 : /* Regular flow */
1457 0 : if (which == XMPP_QUEUE_OLDEST) {
1458 : t = conn->send_queue_head;
1459 0 : } else if (which == XMPP_QUEUE_YOUNGEST) {
1460 : t = conn->send_queue_tail;
1461 : /* search backwards to find last USER element */
1462 0 : while (t && t->owner != XMPP_QUEUE_USER)
1463 0 : t = t->prev;
1464 : } else {
1465 0 : strophe_error(conn->ctx, "conn", "Unknown queue element %d", which);
1466 0 : return NULL;
1467 : }
1468 : /* there was no USER element in the queue */
1469 0 : if (!t)
1470 : return NULL;
1471 :
1472 : /* head is already sent out partially */
1473 0 : if (t == conn->send_queue_head && t->wip && !disconnected)
1474 0 : t = t->next;
1475 :
1476 : /* search forward to find the first USER element */
1477 0 : while (t && t->owner != XMPP_QUEUE_USER)
1478 0 : t = t->next;
1479 :
1480 : /* there was no USER element in the queue we could drop */
1481 0 : if (!t)
1482 : return NULL;
1483 :
1484 : /* In case there exists a SM stanza that is linked to the
1485 : * one we're currently dropping, also delete that one.
1486 : */
1487 0 : if (t->next && t->next->userdata == t) {
1488 0 : strophe_free(conn->ctx, _drop_send_queue_element(conn, t->next));
1489 : /* reset the flag, so we restart to send `<r>` stanzas */
1490 0 : conn->sm_state->r_sent = 0;
1491 : }
1492 : /* Finally drop the element */
1493 0 : return _drop_send_queue_element(conn, t);
1494 : }
1495 :
1496 : /* timed handler for cleanup if normal disconnect procedure takes too long */
1497 0 : static int _disconnect_cleanup(xmpp_conn_t *conn, void *userdata)
1498 : {
1499 0 : UNUSED(userdata);
1500 :
1501 0 : strophe_debug(conn->ctx, "xmpp", "disconnection forced by cleanup timeout");
1502 :
1503 0 : conn_disconnect(conn);
1504 :
1505 0 : return 0;
1506 : }
1507 :
1508 0 : static char *_conn_build_stream_tag(xmpp_conn_t *conn,
1509 : char **attributes,
1510 : size_t attributes_len)
1511 : {
1512 0 : char *tag;
1513 0 : size_t len;
1514 0 : size_t i;
1515 :
1516 0 : static const char *tag_head = "<stream:stream";
1517 0 : static const char *tag_tail = ">";
1518 :
1519 : /* ignore the last element unless number is even */
1520 0 : attributes_len &= ~(size_t)1;
1521 :
1522 0 : len = strlen(tag_head) + strlen(tag_tail);
1523 0 : for (i = 0; i < attributes_len; ++i)
1524 0 : len += strlen(attributes[i]) + 2;
1525 0 : tag = strophe_alloc(conn->ctx, len + 1);
1526 0 : if (!tag)
1527 : return NULL;
1528 :
1529 0 : strcpy(tag, tag_head);
1530 0 : for (i = 0; i < attributes_len; ++i) {
1531 0 : if ((i & 1) == 0) {
1532 0 : strcat(tag, " ");
1533 0 : strcat(tag, attributes[i]);
1534 0 : strcat(tag, "=\"");
1535 : } else {
1536 0 : strcat(tag, attributes[i]);
1537 0 : strcat(tag, "\"");
1538 : }
1539 : }
1540 0 : strcat(tag, tag_tail);
1541 :
1542 0 : if (strlen(tag) != len) {
1543 0 : strophe_error(conn->ctx, "xmpp",
1544 : "Internal error in "
1545 : "_conn_build_stream_tag().");
1546 0 : strophe_free(conn->ctx, tag);
1547 0 : tag = NULL;
1548 : }
1549 :
1550 : return tag;
1551 : }
1552 :
1553 0 : static int _conn_open_stream_with_attributes(xmpp_conn_t *conn,
1554 : char **attributes,
1555 : size_t attributes_len)
1556 : {
1557 0 : char *tag;
1558 :
1559 0 : tag = _conn_build_stream_tag(conn, attributes, attributes_len);
1560 0 : if (!tag)
1561 : return XMPP_EMEM;
1562 :
1563 0 : send_raw_string(conn, "<?xml version=\"1.0\"?>%s", tag);
1564 0 : strophe_free(conn->ctx, tag);
1565 :
1566 0 : return XMPP_EOK;
1567 : }
1568 :
1569 0 : static void _conn_attributes_new(xmpp_conn_t *conn,
1570 : char **attrs,
1571 : char ***attributes,
1572 : size_t *attributes_len)
1573 : {
1574 0 : char **array = NULL;
1575 0 : size_t nr = 0;
1576 0 : size_t i;
1577 :
1578 0 : if (attrs) {
1579 0 : for (; attrs[nr]; ++nr)
1580 : ;
1581 0 : array = strophe_alloc(conn->ctx, sizeof(*array) * nr);
1582 0 : for (i = 0; array && i < nr; ++i) {
1583 0 : array[i] = (i & 1) == 0 ? parser_attr_name(conn->ctx, attrs[i])
1584 0 : : strophe_strdup(conn->ctx, attrs[i]);
1585 0 : if (array[i] == NULL)
1586 : break;
1587 : }
1588 0 : if (!array || i < nr) {
1589 0 : strophe_error(conn->ctx, "xmpp", "Memory allocation error.");
1590 0 : _conn_attributes_destroy(conn, array, i);
1591 0 : array = NULL;
1592 0 : nr = 0;
1593 : }
1594 : }
1595 0 : *attributes = array;
1596 0 : *attributes_len = nr;
1597 0 : }
1598 :
1599 0 : static void _conn_attributes_destroy(xmpp_conn_t *conn,
1600 : char **attributes,
1601 : size_t attributes_len)
1602 : {
1603 0 : size_t i;
1604 :
1605 0 : if (attributes) {
1606 0 : for (i = 0; i < attributes_len; ++i)
1607 0 : strophe_free(conn->ctx, attributes[i]);
1608 0 : strophe_free(conn->ctx, attributes);
1609 : }
1610 0 : }
1611 :
1612 0 : static void _log_open_tag(xmpp_conn_t *conn, char **attrs)
1613 : {
1614 0 : char **attributes;
1615 0 : char *tag;
1616 0 : size_t nr;
1617 :
1618 0 : _conn_attributes_new(conn, attrs, &attributes, &nr);
1619 0 : tag = _conn_build_stream_tag(conn, attributes, nr);
1620 0 : if (tag) {
1621 0 : strophe_debug(conn->ctx, "xmpp", "RECV: %s", tag);
1622 0 : strophe_free(conn->ctx, tag);
1623 : }
1624 0 : _conn_attributes_destroy(conn, attributes, nr);
1625 0 : }
1626 :
1627 0 : static char *_get_stream_attribute(char **attrs, char *name)
1628 : {
1629 0 : int i;
1630 :
1631 0 : if (!attrs)
1632 : return NULL;
1633 :
1634 0 : for (i = 0; attrs[i]; i += 2)
1635 0 : if (strcmp(name, attrs[i]) == 0)
1636 0 : return attrs[i + 1];
1637 :
1638 : return NULL;
1639 : }
1640 :
1641 0 : static void _handle_stream_start(char *name, char **attrs, void *userdata)
1642 : {
1643 0 : xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
1644 0 : char *id;
1645 0 : int failed = 0;
1646 :
1647 0 : if (conn->stream_id)
1648 0 : strophe_free(conn->ctx, conn->stream_id);
1649 0 : conn->stream_id = NULL;
1650 :
1651 0 : if (strcmp(name, "stream") == 0) {
1652 0 : _log_open_tag(conn, attrs);
1653 0 : id = _get_stream_attribute(attrs, "id");
1654 0 : if (id)
1655 0 : conn->stream_id = strophe_strdup(conn->ctx, id);
1656 :
1657 0 : if (id && !conn->stream_id) {
1658 0 : strophe_error(conn->ctx, "conn", "Memory allocation failed.");
1659 0 : failed = 1;
1660 : }
1661 : } else {
1662 0 : strophe_error(conn->ctx, "conn",
1663 : "Server did not open valid stream."
1664 : " name = %s.",
1665 : name);
1666 0 : failed = 1;
1667 : }
1668 :
1669 0 : if (!failed) {
1670 : /* call stream open handler */
1671 0 : conn->open_handler(conn);
1672 : } else {
1673 0 : conn_disconnect(conn);
1674 : }
1675 0 : }
1676 :
1677 0 : static void _handle_stream_end(char *name, void *userdata)
1678 : {
1679 0 : xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
1680 :
1681 0 : UNUSED(name);
1682 :
1683 : /* stream is over */
1684 0 : strophe_debug(conn->ctx, "xmpp", "RECV: </stream:stream>");
1685 : /* the session has been terminated properly, i.e. it can't be resumed */
1686 0 : conn->sm_state->can_resume = 0;
1687 0 : conn_disconnect_clean(conn);
1688 0 : }
1689 :
1690 0 : static void _handle_stream_stanza(xmpp_stanza_t *stanza, void *userdata)
1691 : {
1692 0 : xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
1693 0 : char *buf;
1694 0 : size_t len;
1695 :
1696 0 : if (xmpp_stanza_to_text(stanza, &buf, &len) == 0) {
1697 0 : strophe_debug(conn->ctx, "xmpp", "RECV: %s", buf);
1698 0 : strophe_free(conn->ctx, buf);
1699 : }
1700 :
1701 0 : handler_fire_stanza(conn, stanza);
1702 0 : if (conn->sm_state->sm_enabled)
1703 0 : _conn_sm_handle_stanza(conn, stanza);
1704 0 : }
1705 :
1706 : /* XEP-0198 stream management */
1707 0 : static void _conn_sm_handle_stanza(xmpp_conn_t *const conn,
1708 : xmpp_stanza_t *stanza)
1709 : {
1710 0 : xmpp_stanza_t *a;
1711 0 : xmpp_send_queue_t *e;
1712 0 : char *c;
1713 0 : const char *name, *ns, *attr_h;
1714 0 : char h[11];
1715 0 : unsigned long ul_h;
1716 :
1717 0 : ns = xmpp_stanza_get_ns(stanza);
1718 0 : if (ns && strcmp(ns, XMPP_NS_SM) != 0)
1719 0 : ++conn->sm_state->sm_handled_nr;
1720 : else {
1721 0 : name = xmpp_stanza_get_name(stanza);
1722 0 : if (!name)
1723 0 : return;
1724 0 : if (strcmp(name, "r") == 0) {
1725 0 : a = xmpp_stanza_new(conn->ctx);
1726 0 : if (!a) {
1727 0 : strophe_debug(conn->ctx, "conn", "Couldn't create <a> stanza.");
1728 0 : return;
1729 : }
1730 0 : xmpp_stanza_set_name(a, "a");
1731 0 : xmpp_stanza_set_ns(a, XMPP_NS_SM);
1732 0 : strophe_snprintf(h, sizeof(h), "%u", conn->sm_state->sm_handled_nr);
1733 0 : xmpp_stanza_set_attribute(a, "h", h);
1734 0 : send_stanza(conn, a, XMPP_QUEUE_SM_STROPHE);
1735 0 : } else if (strcmp(name, "a") == 0) {
1736 0 : attr_h = xmpp_stanza_get_attribute(stanza, "h");
1737 0 : if (!attr_h) {
1738 0 : strophe_debug(conn->ctx, "conn", "Didn't find 'h' attribute.");
1739 0 : return;
1740 : }
1741 0 : if (string_to_ul(attr_h, &ul_h)) {
1742 0 : strophe_error(
1743 0 : conn->ctx, "conn",
1744 : "Error on strtoul() of '%s', returned value is %llu.",
1745 : attr_h, ul_h);
1746 : /* We continue here and drop the complete SM queue instead of
1747 : * returning and letting the queue fill up.
1748 : */
1749 0 : ul_h = ULONG_MAX;
1750 : }
1751 0 : while (conn->sm_state->sm_queue.head &&
1752 0 : conn->sm_state->sm_queue.head->sm_h < ul_h) {
1753 0 : e = pop_queue_front(&conn->sm_state->sm_queue);
1754 0 : strophe_debug_verbose(2, conn->ctx, "conn",
1755 : "SM_Q_DROP: %p, h=%lu", e, e->sm_h);
1756 0 : c = queue_element_free(conn->ctx, e);
1757 0 : strophe_free(conn->ctx, c);
1758 : }
1759 0 : conn->sm_state->r_sent = 0;
1760 : }
1761 : }
1762 : }
1763 :
1764 0 : static unsigned short _conn_default_port(xmpp_conn_t *conn,
1765 : xmpp_conn_type_t type)
1766 : {
1767 0 : switch (type) {
1768 0 : case XMPP_CLIENT:
1769 0 : return conn->tls_legacy_ssl ? XMPP_PORT_CLIENT_LEGACY_SSL
1770 0 : : XMPP_PORT_CLIENT;
1771 : case XMPP_COMPONENT:
1772 : return XMPP_PORT_COMPONENT;
1773 : default:
1774 : return 0;
1775 : };
1776 : }
1777 :
1778 0 : char *queue_element_free(xmpp_ctx_t *ctx, xmpp_send_queue_t *e)
1779 : {
1780 0 : char *ret = e->data;
1781 0 : strophe_debug_verbose(2, ctx, "conn", "Q_FREE: %p", e);
1782 0 : memset(e, 0, sizeof(*e));
1783 0 : strophe_free(ctx, e);
1784 0 : strophe_debug_verbose(3, ctx, "conn", "Q_CONTENT: %s", ret);
1785 0 : return ret;
1786 : }
1787 :
1788 8 : static void _conn_reset(xmpp_conn_t *conn)
1789 : {
1790 8 : xmpp_ctx_t *ctx = conn->ctx;
1791 8 : xmpp_send_queue_t *sq, *tsq;
1792 :
1793 8 : if (conn->state != XMPP_STATE_DISCONNECTED) {
1794 0 : strophe_debug(ctx, "conn", "Can't reset connected object.");
1795 0 : return;
1796 : }
1797 :
1798 8 : compression_free(conn);
1799 :
1800 8 : conn->intf = sock_intf;
1801 8 : conn->intf.conn = conn;
1802 :
1803 : /* free queued */
1804 8 : sq = conn->send_queue_head;
1805 8 : while (sq) {
1806 0 : tsq = sq;
1807 0 : sq = sq->next;
1808 0 : strophe_free(ctx, queue_element_free(ctx, tsq));
1809 : }
1810 8 : conn->send_queue_head = NULL;
1811 8 : conn->send_queue_tail = NULL;
1812 8 : conn->send_queue_len = 0;
1813 8 : conn->send_queue_user_len = 0;
1814 :
1815 8 : if (conn->stream_error) {
1816 0 : xmpp_stanza_release(conn->stream_error->stanza);
1817 0 : strophe_free_and_null(ctx, conn->stream_error->text);
1818 0 : strophe_free_and_null(ctx, conn->stream_error);
1819 : }
1820 :
1821 8 : strophe_free_and_null(ctx, conn->domain);
1822 8 : strophe_free_and_null(ctx, conn->bound_jid);
1823 8 : strophe_free_and_null(ctx, conn->stream_id);
1824 8 : conn->stream_negotiation_completed = 0;
1825 8 : conn->secured = 0;
1826 8 : conn->tls_failed = 0;
1827 8 : conn->error = 0;
1828 :
1829 8 : conn->tls_support = 0;
1830 :
1831 8 : conn->bind_required = 0;
1832 8 : conn->session_required = 0;
1833 :
1834 8 : handler_system_delete_all(conn);
1835 : }
1836 :
1837 0 : static int _conn_connect(xmpp_conn_t *conn,
1838 : const char *domain,
1839 : xmpp_conn_type_t type,
1840 : xmpp_conn_handler callback,
1841 : void *userdata)
1842 : {
1843 0 : xmpp_open_handler open_handler;
1844 :
1845 0 : if (conn->state != XMPP_STATE_DISCONNECTED)
1846 : return XMPP_EINVOP;
1847 0 : if (type != XMPP_CLIENT && type != XMPP_COMPONENT)
1848 : return XMPP_EINVOP;
1849 :
1850 0 : _conn_reset(conn);
1851 :
1852 0 : conn->type = type;
1853 0 : conn->domain = strophe_strdup(conn->ctx, domain);
1854 0 : if (!conn->domain)
1855 : return XMPP_EMEM;
1856 :
1857 0 : conn->sock = sock_connect(conn->xsock);
1858 0 : if (conn->sock == INVALID_SOCKET)
1859 : return XMPP_EINT;
1860 :
1861 : /* setup handler */
1862 0 : conn->conn_handler = callback;
1863 0 : conn->userdata = userdata;
1864 :
1865 0 : open_handler = conn->is_raw ? auth_handle_open_stub
1866 0 : : type == XMPP_CLIENT ? auth_handle_open
1867 0 : : auth_handle_component_open;
1868 0 : conn_prepare_reset(conn, open_handler);
1869 :
1870 : /* FIXME: it could happen that the connect returns immediately as
1871 : * successful, though this is pretty unlikely. This would be a little
1872 : * hard to fix, since we'd have to detect and fire off the callback
1873 : * from within the event loop */
1874 :
1875 0 : conn->state = XMPP_STATE_CONNECTING;
1876 0 : conn->timeout_stamp = time_stamp();
1877 :
1878 0 : return 0;
1879 : }
1880 :
1881 0 : void send_raw(xmpp_conn_t *conn,
1882 : const char *data,
1883 : size_t len,
1884 : xmpp_send_queue_owner_t owner,
1885 : void *userdata)
1886 : {
1887 0 : char *d;
1888 :
1889 0 : if (conn->state != XMPP_STATE_CONNECTED)
1890 : return;
1891 :
1892 0 : d = strophe_strndup(conn->ctx, data, len);
1893 0 : if (!d) {
1894 0 : strophe_error(conn->ctx, "conn", "Failed to strndup");
1895 0 : return;
1896 : }
1897 :
1898 0 : _send_raw(conn, d, len, owner, userdata);
1899 : }
1900 :
1901 0 : static void _send_valist(xmpp_conn_t *conn,
1902 : const char *fmt,
1903 : va_list ap,
1904 : xmpp_send_queue_owner_t owner)
1905 : {
1906 0 : va_list apdup;
1907 0 : size_t len;
1908 0 : char buf[1024]; /* small buffer for common case */
1909 0 : char *bigbuf;
1910 :
1911 0 : if (!_is_connected(conn, owner))
1912 0 : return;
1913 :
1914 0 : va_copy(apdup, ap);
1915 0 : len = strophe_vsnprintf(buf, sizeof(buf), fmt, apdup);
1916 0 : va_end(apdup);
1917 :
1918 0 : if (len >= sizeof(buf)) {
1919 : /* we need more space for this data, so we allocate a big
1920 : * enough buffer and print to that */
1921 0 : len++; /* account for trailing \0 */
1922 0 : bigbuf = strophe_alloc(conn->ctx, len);
1923 0 : if (!bigbuf) {
1924 0 : strophe_debug(conn->ctx, "xmpp",
1925 : "Could not allocate memory for send_raw_string");
1926 0 : return;
1927 : }
1928 0 : va_copy(apdup, ap);
1929 0 : strophe_vsnprintf(bigbuf, len, fmt, apdup);
1930 0 : va_end(apdup);
1931 :
1932 : /* len - 1 so we don't send trailing \0 */
1933 0 : _send_raw(conn, bigbuf, len - 1, owner, NULL);
1934 : } else {
1935 : /* go through send_raw() which does the strdup() for us */
1936 0 : send_raw(conn, buf, len, owner, NULL);
1937 : }
1938 : }
1939 :
1940 0 : void send_raw_string(xmpp_conn_t *conn, const char *fmt, ...)
1941 : {
1942 0 : va_list ap;
1943 :
1944 0 : if (conn->state != XMPP_STATE_CONNECTED)
1945 0 : return;
1946 :
1947 0 : va_start(ap, fmt);
1948 0 : _send_valist(conn, fmt, ap, XMPP_QUEUE_SM_STROPHE);
1949 0 : va_end(ap);
1950 : }
1951 :
1952 0 : void send_stanza(xmpp_conn_t *conn,
1953 : xmpp_stanza_t *stanza,
1954 : xmpp_send_queue_owner_t owner)
1955 : {
1956 0 : char *buf = NULL;
1957 0 : size_t len;
1958 :
1959 0 : if (!_is_connected(conn, owner))
1960 0 : goto out;
1961 :
1962 0 : if (xmpp_stanza_to_text(stanza, &buf, &len) != 0) {
1963 0 : strophe_error(conn->ctx, "conn", "Failed to stanza_to_text");
1964 0 : goto out;
1965 : }
1966 :
1967 0 : _send_raw(conn, buf, len, owner, NULL);
1968 0 : out:
1969 0 : xmpp_stanza_release(stanza);
1970 0 : }
1971 :
1972 0 : void add_queue_back(xmpp_queue_t *queue, xmpp_send_queue_t *item)
1973 : {
1974 0 : item->next = NULL;
1975 0 : if (!queue->tail) {
1976 0 : item->prev = NULL;
1977 0 : queue->head = item;
1978 0 : queue->tail = item;
1979 : } else {
1980 0 : item->prev = queue->tail;
1981 0 : queue->tail->next = item;
1982 0 : queue->tail = item;
1983 : }
1984 0 : }
1985 :
1986 0 : xmpp_send_queue_t *peek_queue_front(xmpp_queue_t *queue)
1987 : {
1988 0 : return queue->head;
1989 : }
1990 :
1991 0 : xmpp_send_queue_t *pop_queue_front(xmpp_queue_t *queue)
1992 : {
1993 0 : xmpp_send_queue_t *ret = queue->head;
1994 0 : if (queue->head) {
1995 0 : queue->head = queue->head->next;
1996 0 : if (!queue->head) {
1997 0 : queue->tail = NULL;
1998 : } else {
1999 0 : queue->head->prev = NULL;
2000 : }
2001 0 : ret->prev = ret->next = NULL;
2002 : }
2003 0 : return ret;
2004 : }
2005 :
2006 0 : static int _send_raw(xmpp_conn_t *conn,
2007 : char *data,
2008 : size_t len,
2009 : xmpp_send_queue_owner_t owner,
2010 : void *userdata)
2011 : {
2012 0 : xmpp_send_queue_t *item;
2013 0 : const char *req_ack = "<r xmlns='urn:xmpp:sm:3'/>";
2014 :
2015 : /* create send queue item for queue */
2016 0 : item = strophe_alloc(conn->ctx, sizeof(xmpp_send_queue_t));
2017 0 : if (!item) {
2018 0 : strophe_error(conn->ctx, "conn", "DROPPED: %s", data);
2019 0 : strophe_free(conn->ctx, data);
2020 0 : return XMPP_EMEM;
2021 : }
2022 :
2023 0 : item->data = data;
2024 0 : item->len = len;
2025 0 : item->next = NULL;
2026 0 : item->prev = conn->send_queue_tail;
2027 0 : item->written = 0;
2028 0 : item->wip = 0;
2029 0 : item->userdata = userdata;
2030 0 : item->owner = owner;
2031 :
2032 0 : if (!conn->send_queue_tail) {
2033 : /* first item, set head and tail */
2034 0 : conn->send_queue_head = item;
2035 0 : conn->send_queue_tail = item;
2036 : } else {
2037 : /* add to the tail */
2038 0 : conn->send_queue_tail->next = item;
2039 0 : conn->send_queue_tail = item;
2040 : }
2041 0 : conn->send_queue_len++;
2042 0 : if (owner == XMPP_QUEUE_USER)
2043 0 : conn->send_queue_user_len++;
2044 0 : strophe_debug_verbose(3, conn->ctx, "conn", "QUEUED: %s", data);
2045 0 : strophe_debug_verbose(1, conn->ctx, "conn", "Q_ADD: %p", item);
2046 0 : if (!(owner & XMPP_QUEUE_SM) && conn->sm_state->sm_enabled &&
2047 0 : !conn->sm_state->r_sent) {
2048 0 : send_raw(conn, req_ack, strlen(req_ack), XMPP_QUEUE_SM_STROPHE, item);
2049 0 : conn->sm_state->r_sent = 1;
2050 : }
2051 : return XMPP_EOK;
2052 : }
|